/******************************************************************************
 * Copyright 2007 - 2021 Realtek Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/
#define _HCI_INTF_C_

#include <drv_types.h>
#include <hal_data.h>
#include <platform_ops.h>
#include <sdio_if.h>
#include <harmony_sdio_ops.h>
#ifndef CONFIG_SDIO_HCI
#error "CONFIG_SDIO_HCI shall be on!\n"
#endif

#ifdef CONFIG_RTL8822B
#include <rtl8822b_hal.h>	/* rtl8822bs_set_hal_ops() */
#endif /* CONFIG_RTL8822B */

#ifdef CONFIG_RTL8822C
#include <rtl8822c_hal.h>
#endif /* CONFIG_RTL8822C */

#ifdef CONFIG_PLATFORM_INTEL_BYT
#ifdef CONFIG_ACPI
#include <linux/acpi.h>
#include <linux/acpi_gpio.h>
#include "rtw_android.h"
#endif
static int wlan_en_gpio = -1;
#endif /* CONFIG_PLATFORM_INTEL_BYT */

#ifdef CONFIG_RTL8188F
#define VENDOR_ID_8188FS 0x024c
#define DEVICE_ID_8188FS 0xf179 
int rtw_sdio_id_table[] = {1, VENDOR_ID_8188FS, DEVICE_ID_8188FS};
#endif
#ifdef CONFIG_RTL8821C
#define VENDOR_ID_8821CS 0x024c
#define DEVICE_ID_8821CS 0xc821 
int rtw_sdio_id_table[] = {1, VENDOR_ID_8821CS, DEVICE_ID_8821CS};
#endif

extern struct net_device * rtw_netdev[];
static int rtw_drv_init(DevHandle sdio_handle);
static void rtw_dev_remove(void);
#ifdef CONFIG_SDIO_HOOK_DEV_SHUTDOWN
static void rtw_dev_shutdown(struct device *dev);
#endif

//static int rtw_sdio_suspend(struct device *dev);
//static int rtw_sdio_resume(struct device *dev);

extern void rtw_dev_unload(PADAPTER padapter);

static struct rtw_if_operations sdio_ops = {
	.read		= rtw_sdio_raw_read,
	.write		= rtw_sdio_raw_write,
};

static void sd_sync_int_hdl(void * data)
{
	_adapter *padapter = (_adapter *)rtw_netdev_priv(rtw_netdev[0]);
    
	if (!padapter) {
		RTW_INFO("%s primary adapter == NULL\n", __func__);
		return;
	}

	//rtw_sdio_set_irq_thd(psdpriv, current);
	sd_int_hdl(padapter);
	//rtw_sdio_set_irq_thd(psdpriv, NULL);
}

int rtw_sdio_alloc_irq(struct dvobj_priv *dvobj)
{
	PSDIO_DATA psdio_data;
	DevHandle handle;
	int err;

	psdio_data = &dvobj->intf_data;
	handle = psdio_data->handle;

	SdioClaimHost(handle);

	err = SdioClaimIrq(handle, &sd_sync_int_hdl);

	if (err) {
		dvobj->drv_dbg.dbg_sdio_alloc_irq_error_cnt++;
		RTW_PRINT("%s: sdio_claim_irq FAIL(%d)!\n", __func__, err);
	} else {
		dvobj->drv_dbg.dbg_sdio_alloc_irq_cnt++;
		dvobj->irq_alloc = 1;
	}
	
	SdioReleaseHost(handle);

	return err ? _FAIL : _SUCCESS;
}

void rtw_sdio_free_irq(struct dvobj_priv *dvobj)
{
	PSDIO_DATA psdio_data;
	DevHandle handle;
	int err;

	if (dvobj->irq_alloc) {
		psdio_data = &dvobj->intf_data;
		handle = psdio_data->handle;

		if (handle) {
			SdioClaimHost(handle);
			err = SdioReleaseIrq(handle);
			if (err) {
				dvobj->drv_dbg.dbg_sdio_free_irq_error_cnt++;
				RTW_ERR("%s: sdio_release_irq FAIL(%d)!\n", __func__, err);
			} else
				dvobj->drv_dbg.dbg_sdio_free_irq_cnt++;
			SdioReleaseHost(handle);
		}
		dvobj->irq_alloc = 0;
	}
}

#ifdef CONFIG_GPIO_WAKEUP
extern unsigned int oob_irq;
extern unsigned int oob_gpio;
static irqreturn_t gpio_hostwakeup_irq_thread(int irq, void *data)
{
	PADAPTER padapter = (PADAPTER)data;
	RTW_PRINT("gpio_hostwakeup_irq_thread\n");
	/* Disable interrupt before calling handler */
	/* disable_irq_nosync(oob_irq); */
#ifdef CONFIG_PLATFORM_ARM_SUN6I
	return 0;
#else
	return IRQ_HANDLED;
#endif
}

static u8 gpio_hostwakeup_alloc_irq(PADAPTER padapter)
{
	int err;
	u32 status = 0;

	if (oob_irq == 0) {
		RTW_INFO("oob_irq ZERO!\n");
		return _FAIL;
	}

	RTW_INFO("%s : oob_irq = %d\n", __func__, oob_irq);

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32))
	status = IRQF_NO_SUSPEND;
#endif

	if (HIGH_ACTIVE_DEV2HST)
		status |= IRQF_TRIGGER_RISING;
	else
		status |= IRQF_TRIGGER_FALLING;

	err = request_threaded_irq(oob_irq, gpio_hostwakeup_irq_thread, NULL,
		status, "rtw_wifi_gpio_wakeup", padapter);

	if (err < 0) {
		RTW_INFO("Oops: can't allocate gpio irq %d err:%d\n", oob_irq, err);
		return _FALSE;
	} else
		RTW_INFO("allocate gpio irq %d ok\n", oob_irq);

#ifndef CONFIG_PLATFORM_ARM_SUN8I
	enable_irq_wake(oob_irq);
#endif
	return _SUCCESS;
}

static void gpio_hostwakeup_free_irq(PADAPTER padapter)
{
	wifi_free_gpio(oob_gpio);

	if (oob_irq == 0)
		return;

#ifndef CONFIG_PLATFORM_ARM_SUN8I
	disable_irq_wake(oob_irq);
#endif
	free_irq(oob_irq, padapter);
}
#endif


u32 sdio_init(struct dvobj_priv *dvobj)
{
	PSDIO_DATA psdio_data;
	DevHandle handle;
	int err;
	SdioCommonInfo info = {0};


	psdio_data = &dvobj->intf_data;
	handle = psdio_data->handle;

	/* 3 1. init SDIO bus */
	SdioClaimHost(handle);

	err = SdioEnableFunc(handle);
	if (err) {
		RTW_PRINT("%s: SdioEnableFunc FAIL(%d)!\n", __func__, err);
		goto release;
	}

	err = SdioSetBlockSize(handle, 512);
	if (err) {
		RTW_PRINT("%s: SdioSetBlockSize FAIL(%d)!\n", __func__, err);
		goto release;
	}
	psdio_data->block_transfer_len = 512;
	psdio_data->tx_block_mode = 1;
	psdio_data->rx_block_mode = 1;

	//psdio_data->sd3_bus_mode = _FALSE;
	psdio_data->sd3_bus_mode = _TRUE;
	SdioGetCommonInfo(handle,&info,SDIO_FUNC_INFO);	

release:
	SdioReleaseHost(handle);

	if (err)
		return _FAIL;
	return _SUCCESS;
}

void sdio_deinit(struct dvobj_priv *dvobj)
{
	int err;
	DevHandle handle;
	PSDIO_DATA psdio;
	psdio = &dvobj->intf_data;

	handle = psdio->handle;

	RTW_INFO("+sdio_deinit\n");

	if (handle!=NULL) {
		SdioClaimHost(handle);
		//err = SdioReleaseIrq(handle);
	
		err = SdioDisableFunc(handle);
		if (err)
		{
			dvobj->drv_dbg.dbg_sdio_deinit_error_cnt++;
			RTW_DBG(KERN_ERR "%s: sdio_disable_func(%d)\n", __func__, err);
		}
		if (dvobj->irq_alloc) {
			err = SdioReleaseIrq(handle);
			if (err)
			{
				dvobj->drv_dbg.dbg_sdio_free_irq_error_cnt++;
				RTW_DBG(KERN_ERR "%s: sdio_release_irq(%d)\n", __func__, err);
			}
			else
				dvobj->drv_dbg.dbg_sdio_free_irq_cnt++;
		}
		SdioReleaseHost(handle);
		SdioClose(handle);
	}
}

u8 sdio_get_num_of_func(struct dvobj_priv *dvobj)
{
	return dvobj->intf_data.func_number;
}

static void rtw_decide_chip_type_by_device_id(struct dvobj_priv *dvobj, int  *sdio_id_table)
{
	int vendor_id = 0,device_id = 0;

	vendor_id = sdio_id_table[1];
	device_id = sdio_id_table[2];

#if defined(CONFIG_RTL8188F)
	if ((vendor_id == VENDOR_ID_8188FS) && (device_id == DEVICE_ID_8188FS)) {
		dvobj->HardwareType = HARDWARE_TYPE_RTL8188FS;
		dvobj->chip_type = RTL8188F;
		RTW_PRINT("CHIP TYPE: RTL8188F\n");
	}
#endif
#if defined(CONFIG_RTL8821C)
	if ((vendor_id == VENDOR_ID_8821CS) && (device_id == DEVICE_ID_8821CS)) {
		dvobj->HardwareType = HARDWARE_TYPE_RTL8821CS;
		dvobj->chip_type = RTL8821C;
		RTW_PRINT("CHIP TYPE: RTL8821C\n");
	}
#endif

}

static struct dvobj_priv *sdio_dvobj_init(DevHandle handle)
{
	int status = _FAIL;
	struct dvobj_priv *dvobj = NULL;
	PSDIO_DATA psdio;


	if((dvobj = devobj_init()) == NULL) {
		goto exit;
	}
	dvobj->intf_ops = &sdio_ops;

	psdio = &dvobj->intf_data;
	psdio->handle = handle;

	if (sdio_init(dvobj) != _SUCCESS) {
		RTW_INFO("%s: initialize SDIO Failed!\n", __FUNCTION__);
		goto free_dvobj;
	}

	dvobj->interface_type = RTW_SDIO;
	rtw_decide_chip_type_by_device_id(dvobj, rtw_sdio_id_table);

	rtw_reset_continual_io_error(dvobj);
	status = _SUCCESS;

free_dvobj:
	if (status != _SUCCESS && dvobj) {
		devobj_deinit(dvobj);
		
		dvobj = NULL;
	}
exit:

	return dvobj;
}

static void sdio_dvobj_deinit(struct dvobj_priv *dvobj)
{

	if (dvobj) {
		sdio_deinit(dvobj);
		devobj_deinit(dvobj);
	}


	//return;
}

u8 rtw_set_hal_ops(PADAPTER padapter)
{
	/* alloc memory for HAL DATA */
	if (rtw_hal_data_init(padapter) == _FAIL)
		return _FAIL;

#if defined(CONFIG_RTL8188E)
	if (rtw_get_chip_type(padapter) == RTL8188E)
		rtl8188es_set_hal_ops(padapter);
#endif

#if defined(CONFIG_RTL8723B)
	if (rtw_get_chip_type(padapter) == RTL8723B)
		rtl8723bs_set_hal_ops(padapter);
#endif

#if defined(CONFIG_RTL8821A)
	if (rtw_get_chip_type(padapter) == RTL8821)
		rtl8821as_set_hal_ops(padapter);
#endif

#if defined(CONFIG_RTL8192E)
	if (rtw_get_chip_type(padapter) == RTL8192E)
		rtl8192es_set_hal_ops(padapter);
#endif

#if defined(CONFIG_RTL8703B)
	if (rtw_get_chip_type(padapter) == RTL8703B)
		rtl8703bs_set_hal_ops(padapter);
#endif

#if defined(CONFIG_RTL8723D)
	if (rtw_get_chip_type(padapter) == RTL8723D)
		rtl8723ds_set_hal_ops(padapter);
#endif

#if defined(CONFIG_RTL8188F)
	if (rtw_get_chip_type(padapter) == RTL8188F)
		rtl8188fs_set_hal_ops(padapter);
#endif

#if defined(CONFIG_RTL8188GTV)
	if (rtw_get_chip_type(padapter) == RTL8188GTV)
		rtl8188gtvs_set_hal_ops(padapter);
#endif

#if defined(CONFIG_RTL8822B)
	if (rtw_get_chip_type(padapter) == RTL8822B)
		rtl8822bs_set_hal_ops(padapter);
#endif

#if defined(CONFIG_RTL8821C)
	if (rtw_get_chip_type(padapter) == RTL8821C) {
		RTW_PRINT("rtl8821cs_set_hal_ops\n");
		if (rtl8821cs_set_hal_ops(padapter) == _FAIL)
			return _FAIL;
	}
#endif

#if defined(CONFIG_RTL8192F)
	if (rtw_get_chip_type(padapter) == RTL8192F)
		rtl8192fs_set_hal_ops(padapter);
#endif

#if defined(CONFIG_RTL8822C)
	if (rtw_get_chip_type(padapter) == RTL8822C)
		rtl8822cs_set_hal_ops(padapter);
#endif

	if (rtw_hal_ops_check(padapter) == _FAIL)
		return _FAIL;

	if (hal_spec_init(padapter) == _FAIL)
		return _FAIL;

	return _SUCCESS;
}

static void sd_intf_start(PADAPTER padapter)
{
	if (padapter == NULL) {
		RTW_ERR("%s: padapter is NULL!\n", __func__);
		return;
	}

	/* hal dep */
	rtw_hal_enable_interrupt(padapter);
}

static void sd_intf_stop(PADAPTER padapter)
{
	if (padapter == NULL) {
		RTW_ERR("%s: padapter is NULL!\n", __func__);
		return;
	}

	/* hal dep */
	rtw_hal_disable_interrupt(padapter);
}


#ifdef RTW_SUPPORT_PLATFORM_SHUTDOWN
PADAPTER g_test_adapter = NULL;
#endif /* RTW_SUPPORT_PLATFORM_SHUTDOWN */

_adapter *rtw_sdio_primary_adapter_init(struct dvobj_priv *dvobj)
{
	int status = _FAIL;
	PADAPTER padapter = NULL;

	padapter = (_adapter *)rtw_zvmalloc(sizeof(*padapter));
	if (padapter == NULL)
		goto exit;

	if (loadparam(padapter) != _SUCCESS)
		goto free_adapter;

#ifdef RTW_SUPPORT_PLATFORM_SHUTDOWN
	g_test_adapter = padapter;
#endif /* RTW_SUPPORT_PLATFORM_SHUTDOWN */
	padapter->dvobj = dvobj;

	rtw_set_drv_stopped(padapter);/*init*/

	dvobj->padapters[dvobj->iface_nums++] = padapter;
	padapter->iface_id = IFACE_ID0;

	/* set adapter_type/iface type for primary padapter */
	padapter->isprimary = _TRUE;
	padapter->adapter_type = PRIMARY_ADAPTER;
#ifdef CONFIG_MI_WITH_MBSSID_CAM
	padapter->hw_port = HW_PORT0;
#else
	padapter->hw_port = HW_PORT0;
#endif

	/* 3 3. init driver special setting, interface, OS and hardware relative */

	/* 4 3.1 set hardware operation functions */
	if (rtw_set_hal_ops(padapter) == _FAIL)
		goto free_hal_data;

	/* 3 5. initialize Chip version */
	padapter->intf_start = &sd_intf_start;
	padapter->intf_stop = &sd_intf_stop;

	if (rtw_init_io_priv(padapter, sdio_set_intf_ops) == _FAIL) {
		goto free_hal_data;
	}

	rtw_hal_read_chip_version(padapter);

	rtw_hal_chip_configure(padapter);

#ifdef CONFIG_BT_COEXIST
	rtw_btcoex_Initialize(padapter);
#endif
	rtw_btcoex_wifionly_initialize(padapter);

	/* 3 6. read efuse/eeprom data */
	if (rtw_hal_read_chip_info(padapter) == _FAIL)
		goto free_hal_data;

	/* 3 7. init driver common data */
	if (rtw_init_drv_sw(padapter) == _FAIL) {
		goto free_hal_data;
	}

	/* 3 8. get WLan MAC address */
	/* set mac addr */
	rtw_macaddr_cfg(adapter_mac_addr(padapter),  get_hal_mac_addr(padapter));

#ifdef CONFIG_MI_WITH_MBSSID_CAM
	rtw_mbid_camid_alloc(padapter, adapter_mac_addr(padapter));
#endif
#ifdef CONFIG_P2P
	rtw_init_wifidirect_addrs(padapter, adapter_mac_addr(padapter), adapter_mac_addr(padapter));
#endif /* CONFIG_P2P */

	rtw_hal_disable_interrupt(padapter);

	RTW_INFO("bDriverStopped:%s, bSurpriseRemoved:%s, bup:%d, hw_init_completed:%d\n"
		, rtw_is_drv_stopped(padapter) ? "True" : "False"
		, rtw_is_surprise_removed(padapter) ? "True" : "False"
		, padapter->bup
		, rtw_get_hw_init_completed(padapter)
	);

	status = _SUCCESS;

free_hal_data:
	if (status != _SUCCESS && padapter->HalData)
		rtw_hal_free_data(padapter);

free_adapter:
	if (status != _SUCCESS && padapter) {
		#ifdef RTW_HALMAC
		rtw_halmac_deinit_adapter(dvobj);
		#endif
		rtw_vmfree((u8 *)padapter, sizeof(*padapter));
		padapter = NULL;
	}
exit:
	return padapter;
}

static void rtw_sdio_primary_adapter_deinit(_adapter *padapter)
{
	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;

	if (check_fwstate(pmlmepriv, _FW_LINKED))
		rtw_disassoc_cmd(padapter, 0, RTW_CMDF_DIRECTLY);

#ifdef CONFIG_AP_MODE
	if (MLME_IS_AP(padapter) || MLME_IS_MESH(padapter)) {
		free_mlme_ap_info(padapter);
		#ifdef CONFIG_HOSTAPD_MLME
		hostapd_mode_unload(padapter);
		#endif
	}
#endif

#ifdef CONFIG_GPIO_WAKEUP
#ifdef CONFIG_PLATFORM_ARM_SUN6I
	sw_gpio_eint_set_enable(gpio_eint_wlan, 0);
	sw_gpio_irq_free(eint_wlan_handle);
#else
	gpio_hostwakeup_free_irq(padapter);
#endif
#endif

	/*rtw_cancel_all_timer(if1);*/

#ifdef CONFIG_WOWLAN
	adapter_to_pwrctl(padapter)->wowlan_mode = _FALSE;
	RTW_PRINT("%s wowlan_mode:%d\n", __func__, adapter_to_pwrctl(padapter)->wowlan_mode);
#endif /* CONFIG_WOWLAN */

	rtw_dev_unload(padapter);
	RTW_INFO("+r871xu_dev_remove, hw_init_completed=%d\n", rtw_get_hw_init_completed(padapter));

	rtw_free_drv_sw(padapter);

	/* TODO: use rtw_os_ndevs_deinit instead at the first stage of driver's dev deinit function */
	rtw_os_ndev_free(padapter);

#ifdef RTW_HALMAC
	rtw_halmac_deinit_adapter(adapter_to_dvobj(padapter));
#endif /* RTW_HALMAC */

	rtw_vmfree((u8 *)padapter, sizeof(_adapter));

#ifdef CONFIG_PLATFORM_RTD2880B
	RTW_INFO("wlan link down\n");
	rtd2885_wlan_netlink_sendMsg("linkdown", "8712");
#endif

#ifdef RTW_SUPPORT_PLATFORM_SHUTDOWN
	g_test_adapter = NULL;
#endif /* RTW_SUPPORT_PLATFORM_SHUTDOWN */
}

_adapter *rtw_sdio_if1_init(struct dvobj_priv *dvobj)
{
	int status = _FAIL;
	PADAPTER padapter = NULL;

	padapter = (_adapter *)rtw_zvmalloc(sizeof(*padapter));
	if (padapter == NULL)
		goto exit;

	if (loadparam(padapter) != _SUCCESS)
		goto free_adapter;

#ifdef RTW_SUPPORT_PLATFORM_SHUTDOWN
	g_test_adapter = padapter;
#endif // RTW_SUPPORT_PLATFORM_SHUTDOWN
	padapter->dvobj = dvobj;

	rtw_set_drv_stopped(padapter);/*init*/

	dvobj->padapters[dvobj->iface_nums++] = padapter;
	padapter->iface_id = IFACE_ID0;

#if defined(CONFIG_CONCURRENT_MODE)
	//set adapter_type/iface type for primary padapter
	padapter->isprimary = _TRUE;
	padapter->adapter_type = PRIMARY_ADAPTER;	
	#ifndef CONFIG_HWPORT_SWAP
	padapter->iface_type = IFACE_PORT0;
	#else
	padapter->iface_type = IFACE_PORT1;
	#endif
#endif

	//3 3. init driver special setting, interface, OS and hardware relative

	//4 3.1 set hardware operation functions
	if (rtw_set_hal_ops(padapter)== _FAIL)
		goto free_hal_data;

	//3 5. initialize Chip version
	padapter->intf_start = &sd_intf_start;
	padapter->intf_stop = &sd_intf_stop;

	padapter->intf_init = &sdio_init;
	padapter->intf_deinit = &sdio_deinit;
	padapter->intf_alloc_irq = &rtw_sdio_alloc_irq;
	padapter->intf_free_irq = &rtw_sdio_free_irq;

	if (rtw_init_io_priv(padapter, sdio_set_intf_ops) == _FAIL)
	{
		RTW_INFO("rtw_drv_init: Can't init io_priv\n");
		goto free_hal_data;
	}

	rtw_hal_read_chip_version(padapter);
	rtw_hal_chip_configure(padapter);
#ifdef CONFIG_BT_COEXIST
	rtw_btcoex_Initialize(padapter);
#endif // CONFIG_BT_COEXIST

	rtw_btcoex_wifionly_initialize(padapter);

	//3 6. read efuse/eeprom data
	rtw_hal_read_chip_info(padapter);
		
	//3 7. init driver common data
	if (rtw_init_drv_sw(padapter) == _FAIL) {
		RTW_INFO("rtw_drv_init: Initialize driver software resource Failed!\n");
		goto free_hal_data;
	}
	//3 8. get WLan MAC address
	// set mac addr
	rtw_macaddr_cfg(adapter_mac_addr(padapter),  get_hal_mac_addr(padapter));
#ifdef CONFIG_P2P
	rtw_init_wifidirect_addrs(padapter, adapter_mac_addr(padapter), adapter_mac_addr(padapter));
#endif /* CONFIG_P2P */

	rtw_hal_disable_interrupt(padapter);
	RTW_INFO("bDriverStopped:%s, bSurpriseRemoved:%s, bup:%d, hw_init_completed:%d\n"
		, rtw_is_drv_stopped(padapter)?"True":"False"
		, rtw_is_surprise_removed(padapter)?"True":"False"
		, padapter->bup
		, rtw_get_hw_init_completed(padapter)
	);
	
	status = _SUCCESS;
	
free_hal_data:
	if (status != _SUCCESS && padapter->HalData)
	{
		RTW_INFO("free_hal_data\n");
		rtw_hal_free_data(padapter);
	}
free_adapter:
	if (status != _SUCCESS && padapter) 
	{	
		RTW_INFO("free_adapter\n");
		rtw_vmfree((u8 *)padapter, sizeof(*padapter));
		padapter = NULL;
	}
exit:
	return padapter;
}

static void rtw_sdio_if1_deinit(_adapter *if1)
{
	struct mlme_priv *pmlmepriv= &if1->mlmepriv;

	if(check_fwstate(pmlmepriv, _FW_LINKED))
		rtw_disassoc_cmd(if1, 0, _FALSE);

#ifdef CONFIG_AP_MODE
	if (MLME_IS_AP(if1) || MLME_IS_MESH(if1)) {
		free_mlme_ap_info(if1);
		#ifdef CONFIG_HOSTAPD_MLME
		hostapd_mode_unload(if1);
		#endif
	}
#endif

#ifdef CONFIG_GPIO_WAKEUP
#ifdef CONFIG_PLATFORM_ARM_SUN6I 
		sw_gpio_eint_set_enable(gpio_eint_wlan, 0);
		sw_gpio_irq_free(eint_wlan_handle);
#else  
	gpio_hostwakeup_free_irq(if1);
#endif
#endif


#ifdef CONFIG_WOWLAN
	adapter_to_pwrctl(padapter)->wowlan_mode = _FALSE;
	RTW_PRINT("%s wowlan_mode:%d\n", __func__, adapter_to_pwrctl(padapter)->wowlan_mode);
#endif /* CONFIG_WOWLAN */

	rtw_dev_unload(if1);
	RTW_DBG("+r871xu_dev_remove, hw_init_completed=%d\n", rtw_get_hw_init_completed(if1));


	rtw_free_drv_sw(if1);

	/* TODO: use rtw_os_ndevs_deinit instead at the first stage of driver's dev deinit function */
	rtw_os_ndev_free(if1);

	rtw_vmfree((u8 *)if1, sizeof(_adapter));

#ifdef CONFIG_PLATFORM_RTD2880B
	RTW_DBG("wlan link down\n");
	rtd2885_wlan_netlink_sendMsg("linkdown", "8712");
#endif

#ifdef RTW_SUPPORT_PLATFORM_SHUTDOWN
	g_test_adapter = NULL;
#endif // RTW_SUPPORT_PLATFORM_SHUTDOWN
}


/*
 * drv_init() - a device potentially for us
 *
 * notes: drv_init() is called when the bus driver has located a card for us to support.
 *        We accept the new device by returning 0.
 */
static int rtw_drv_init(DevHandle sdio_handle)
{
	int status = _FAIL;
	struct net_device *pnetdev;
	PADAPTER if1 = NULL, if2 = NULL;
	u8 value8;
	u16 value16;
	u32 value32;
	struct dvobj_priv *dvobj;

#ifdef CONFIG_PLATFORM_INTEL_BYT

#ifdef CONFIG_ACPI
		acpi_handle handle;
		struct acpi_device *adev;
#endif

#if defined(CONFIG_ACPI) && defined(CONFIG_GPIO_WAKEUP)
	handle = ACPI_HANDLE(&func->dev);

	if (handle) {
		/* Dont try to do acpi pm for the wifi module */
		if (!handle || acpi_bus_get_device(handle, &adev))
			RTW_DBG("Could not get acpi pointer!\n");
		else {
			adev->flags.power_manageable = 0;
			RTW_DBG("Disabling ACPI power management support!\n");
		}
		oob_gpio = acpi_get_gpio_by_index(&func->dev, 0, NULL);
		RTW_DBG("rtw_drv_init: ACPI_HANDLE found oob_gpio %d!\n", oob_gpio);
		wifi_configure_gpio();
	}
	else
		RTW_DBG("rtw_drv_init: ACPI_HANDLE NOT found!\n");
#endif

#if defined(CONFIG_ACPI)
	if (&func->dev && ACPI_HANDLE(&func->dev)) {
		wlan_en_gpio = acpi_get_gpio_by_index(&func->dev, 1, NULL);
		RTW_DBG("rtw_drv_init: ACPI_HANDLE found wlan_en %d!\n", wlan_en_gpio);
	}
	else
		RTW_DBG("rtw_drv_init: ACPI_HANDLE NOT found!\n");
#endif
#endif //CONFIG_PLATFORM_INTEL_BYT


	if ((dvobj = sdio_dvobj_init(sdio_handle/*, id*/)) == NULL) {
		RTW_INFO("initialize device object priv Failed!\n");
		goto exit;
	}

	if ((if1 = rtw_sdio_if1_init(dvobj)) == NULL) {
		RTW_DBG("rtw_init_primary_adapter Failed!\n");
		goto free_dvobj;
	}

#ifdef CONFIG_CONCURRENT_MODE
	if ((if2 = rtw_drv_if2_init(if1, sdio_set_intf_ops)) == NULL) {
		goto free_if1;
	}
#endif

	//dev_alloc_name && register_netdev
	if (rtw_os_ndevs_init(dvobj) != _SUCCESS)
		goto free_if2;


#ifdef CONFIG_HOSTAPD_MLME

	hostapd_mode_init(if1);
#endif

#ifdef CONFIG_PLATFORM_RTD2880B
	RTW_DBG("wlan link up\n");
	rtd2885_wlan_netlink_sendMsg("linkup", "8712");
#endif
	if (rtw_sdio_alloc_irq(dvobj) != _SUCCESS)
		goto os_ndevs_deinit;

#ifdef	CONFIG_GPIO_WAKEUP
#ifdef CONFIG_PLATFORM_ARM_SUN6I
		eint_wlan_handle = sw_gpio_irq_request(gpio_eint_wlan, TRIG_EDGE_NEGATIVE,(peint_handle)gpio_hostwakeup_irq_thread, NULL);
		if (!eint_wlan_handle) {
			   RTW_DBG( "%s: request irq failed\n",__func__);
			   return -1;
  }
#else
	gpio_hostwakeup_alloc_irq(if1);
#endif
#endif

#ifdef CONFIG_GLOBAL_UI_PID
	if(ui_pid[1]!=0) {
		RTW_DBG("ui_pid[1]:%d\n",ui_pid[1]);
		rtw_signal_process(ui_pid[1], SIGUSR2);
	}
#endif

	RTW_INFO("-871x_drv - drv_init, success!\n");

	status = _SUCCESS;


os_ndevs_deinit:
	if (status != _SUCCESS)
		rtw_os_ndevs_deinit(dvobj);
free_if2:
	if(status != _SUCCESS && if2) {
		#ifdef CONFIG_CONCURRENT_MODE
		rtw_drv_if2_stop(if2);
		rtw_drv_if2_free(if2);
		#endif
	}
free_if1:
	if (status != _SUCCESS && if1) {
		rtw_sdio_if1_deinit(if1);
	}

free_dvobj:
	if (status != _SUCCESS)
	{
		sdio_dvobj_deinit(sdio_handle);
	}

exit:
	return status == _SUCCESS?0:-ENODEV;
}

static void rtw_dev_remove(void)
{
	//struct dvobj_priv *dvobj = sdio_get_drvdata(func);
	_adapter *padapter = (_adapter *)rtw_netdev_priv(rtw_netdev[0]);
	struct dvobj_priv *dvobj = padapter->dvobj;
	
	struct pwrctrl_priv *pwrctl = dvobj_to_pwrctl(dvobj);
//	PADAPTER padapter = dvobj->padapters[IFACE_ID0];
	PSDIO_DATA sdio_data = &dvobj->intf_data;
	DevHandle handle = sdio_data->handle;

	RTW_INFO("+rtw_dev_remove\n");

	dvobj->processing_dev_remove = _TRUE;

	/* TODO: use rtw_os_ndevs_deinit instead at the first stage of driver's dev deinit function */
	rtw_os_ndevs_unregister(dvobj);

	if (!rtw_is_surprise_removed(padapter)) {
		int err;
		u8 data;
		/* test surprise remove */
		SdioClaimHost(handle);
		err = SdioReadBytes(handle,&data, 0, 1, 0); 
		SdioReleaseHost(handle);
		if (err == -ENOMEDIUM) {
			rtw_set_surprise_removed(padapter);
			RTW_DBG(KERN_NOTICE "%s: device had been removed!\n", __func__);
		}
	}

#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_ANDROID_POWER)
	rtw_unregister_early_suspend(pwrctl);
#endif

	if (GET_HAL_DATA(padapter)->bFWReady == _TRUE) {
		rtw_ps_deny(padapter, PS_DENY_DRV_REMOVE);
		rtw_pm_set_ips(padapter, IPS_NONE);
		rtw_pm_set_lps(padapter, PS_MODE_ACTIVE);
		LeaveAllPowerSaveMode(padapter);
	}
	//rtw_set_drv_stopped(padapter);	/*for stop thread*/
#ifdef CONFIG_CONCURRENT_MODE
	rtw_drv_if2_stop(dvobj->padapters[IFACE_ID1]);
#endif

#ifdef CONFIG_BT_COEXIST
	rtw_btcoex_HaltNotify(padapter);
#endif

	rtw_sdio_if1_deinit(padapter);

#ifdef CONFIG_CONCURRENT_MODE
	rtw_drv_if2_free(dvobj->padapters[IFACE_ID1]);
#endif
				
	sdio_dvobj_deinit(dvobj);

	RTW_INFO("-rtw_dev_remove\n");

}


extern int pm_netdev_open(struct net_device *pnetdev, u8 bnormal);
extern int pm_netdev_close(struct net_device *pnetdev, u8 bnormal);

static int rtw_sdio_suspend(struct device *dev)
{
//	struct sdio_func *func =dev_to_sdio_func(dev);
//	struct dvobj_priv *psdpriv = sdio_get_drvdata(func);
	_adapter *padapter = (_adapter *)rtw_netdev_priv(rtw_netdev[0]);
	struct dvobj_priv *psdpriv = padapter->dvobj;
	PSDIO_DATA sdio_data = &psdpriv->intf_data;
	//struct sdio_func *func = sdio_data->func;

	struct pwrctrl_priv *pwrpriv = NULL;
//	_adapter *padapter = NULL;
	struct debug_priv *pdbgpriv = NULL;
	int ret = 0;
	u8 ch, bw, offset;

	if (psdpriv == NULL)
		goto exit;

	pwrpriv = dvobj_to_pwrctl(psdpriv);
//	padapter = psdpriv->padapters[IFACE_ID0];
	pdbgpriv = &psdpriv->drv_dbg;
	if (rtw_is_drv_stopped(padapter)) {
		DBG_871X("%s bDriverStopped == _TRUE\n", __func__);
		goto exit;
	}

	if (pwrpriv->bInSuspend == _TRUE)
	{
		DBG_871X("%s bInSuspend = %d\n", __func__, pwrpriv->bInSuspend);
		pdbgpriv->dbg_suspend_error_cnt++;
		goto exit;
	}

	ret = rtw_suspend_common(padapter); 	

exit:
#ifdef CONFIG_RTW_SDIO_PM_KEEP_POWER 
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34))
	//Android 4.0 don't support WIFI close power
	//or power down or clock will close after wifi resume,
	//this is sprd's bug in Android 4.0, but sprd don't
	//want to fix it.
	//we have test power under 8723as, power consumption is ok
	#if 0  //for harmony no this sdio func
	if (func) {
		mmc_pm_flag_t pm_flag = 0;
		pm_flag = sdio_get_host_pm_caps(func);
		DBG_871X("cmd: %s: suspend: PM flag = 0x%x\n", sdio_func_id(func), pm_flag);
		if (!(pm_flag & MMC_PM_KEEP_POWER)) {
			DBG_871X("%s: cannot remain alive while host is suspended\n", sdio_func_id(func));
			if (pdbgpriv)
				pdbgpriv->dbg_suspend_error_cnt++;
			return -ENOSYS;
		} else {
			DBG_871X("cmd: suspend with MMC_PM_KEEP_POWER\n");
			sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
		}
	}
	#endif
#endif	
#endif
	return ret;
}
int rtw_resume_process(_adapter *padapter)
{
	struct pwrctrl_priv *pwrpriv = adapter_to_pwrctl(padapter);
	struct dvobj_priv *psdpriv = padapter->dvobj;
	struct debug_priv *pdbgpriv = &psdpriv->drv_dbg;

	if (pwrpriv->bInSuspend == _FALSE) {
		pdbgpriv->dbg_resume_error_cnt++;
		RTW_INFO("%s bInSuspend = %d\n", __FUNCTION__, pwrpriv->bInSuspend);
		return -1;
	}

	return rtw_resume_common(padapter);
}

static int rtw_sdio_resume(struct device *dev)
{
//	struct sdio_func *func =dev_to_sdio_func(dev);
//	struct dvobj_priv *psdpriv = sdio_get_drvdata(func);
	_adapter *padapter = (_adapter *)rtw_netdev_priv(rtw_netdev[0]);
	struct dvobj_priv *psdpriv = padapter->dvobj;

	struct pwrctrl_priv *pwrpriv = dvobj_to_pwrctl(psdpriv);
//	_adapter *padapter = psdpriv->padapters[IFACE_ID0];
	struct mlme_ext_priv	*pmlmeext = &padapter->mlmeextpriv;
	int ret = 0;
	struct debug_priv *pdbgpriv = &psdpriv->drv_dbg;

//	RTW_DBG("==> %s (%s:%d)\n",__FUNCTION__, current->comm, current->pid);

	pdbgpriv->dbg_resume_cnt++;
#ifdef CONFIG_AUTOSUSPEND
	if(pwrpriv->bInternalAutoSuspend)
	{
		ret = rtw_resume_process(padapter);
	}
	else
#endif
	{
#ifdef CONFIG_PLATFORM_INTEL_BYT
		if(0)
#else
		if(pwrpriv->wowlan_mode || pwrpriv->wowlan_ap_mode)
#endif
		{
			rtw_resume_lock_suspend();			
			ret = rtw_resume_process(padapter);
			rtw_resume_unlock_suspend();
		}
		else
		{
#ifdef CONFIG_RESUME_IN_WORKQUEUE
			rtw_resume_in_workqueue(pwrpriv);
#else			
			if (rtw_is_earlysuspend_registered(pwrpriv))
			{
				/* jeff: bypass resume here, do in late_resume */
				rtw_set_do_late_resume(pwrpriv, _TRUE);
			}	
			else
			{
				rtw_resume_lock_suspend();			
				ret = rtw_resume_process(padapter);
				rtw_resume_unlock_suspend();
			}
#endif
		}
	}
	pmlmeext->last_scan_time = rtw_get_current_time();
	RTW_DBG("<========  %s return %d\n", __FUNCTION__, ret);
	return ret;

}

int rtw_drv_entry(void)
{
	int ret = 0;
	int SDIO_NUM = SDIO_BUS_NUM;
	DevHandle sdio_handle = NULL;

	RTW_PRINT("module init start\n");
	dump_drv_version(RTW_DBGDUMP);
#ifdef BTCOEXVERSION
	RTW_PRINT(DRV_NAME" BT-Coex version = %s\n", BTCOEXVERSION);
#endif // BTCOEXVERSION

	ret = platform_wifi_power_on();
	if (ret)
	{
		RTW_DBG("%s: power on failed!!(%d)\n", __FUNCTION__, ret);
		ret = -1;
		goto exit;
	}

	sdio_handle = SdioOpen(SDIO_NUM);

	if(sdio_handle == NULL)
	{
		RTW_DBG("probe sdio failed\n");
		ret = -1;
		goto exit;
	}
	rtw_drv_init(sdio_handle);

#ifndef CONFIG_PLATFORM_INTEL_BYT
	rtw_android_wifictrl_func_add();
#endif //!CONFIG_PLATFORM_INTEL_BYT
	goto exit;

poweroff:
	platform_wifi_power_off();

exit:
	RTW_PRINT("module init ret=%d\n", ret);
	return ret;
}

void rtw_drv_halt(void)
{
	RTW_PRINT("module exit start\n");

#ifndef CONFIG_PLATFORM_INTEL_BYT
	rtw_android_wifictrl_func_del();
#endif 
	rtw_dev_remove();
	platform_wifi_power_off();
	
	RTW_PRINT("module exit success\n");

	rtw_mstat_dump(RTW_DBGDUMP);

	_rtw_memset(rtw_netdev,0,sizeof(rtw_netdev[0]));
}

#ifdef CONFIG_PLATFORM_INTEL_BYT
int rtw_sdio_set_power(int on)
{

	if (wlan_en_gpio >= 0) {
		if (on)
			gpio_set_value(wlan_en_gpio, 1);
		else
			gpio_set_value(wlan_en_gpio, 0);
	}

	return 0;
}
#endif /* CONFIG_PLATFORM_INTEL_BYT */
